﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="YellowFish">
//   Copyright © YellowFish 2009
// </copyright>
// <summary>
//   Defines the Program type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using Diagnostics.Annotations;
using MobilePal.ProcessorAgents;

namespace MobilePal
{
  #region Imported Namespaces


  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Reflection;
  using System.Windows.Forms;
  using System.Xml.Serialization;

  using Dialogs;

  using Log;

  using State;

  using Views.ButtonsView;


  #endregion


  /// <summary>
  /// A main Mobile Pal program class.
  /// </summary>
  internal static class Program
  {
    #region Constants and Fields


    private static List<ActivityRuleClassInfo> _ruleClassInfos;
    private static List<ActivityRuleSettingsControlClassInfo> _ruleSettingsControlsClassInfos;
    private static ApplicationState _state;
    private static List<ApplicationClassInfo> _viewClassInfos;


    #endregion


    #region Public Methods


    /// <summary>
    /// Creates the set time dialog.
    /// </summary>
    /// <param name="minValue">
    /// The minimum time value.
    /// </param>
    /// <param name="maxValue">
    /// The max value.
    /// </param>
    /// <returns>
    /// A new instance of <see cref="SetTimeDialog"/>.
    /// </returns>
    [NotNull]
    public static SetTimeDialog CreateSetTimeDialog(DateTime minValue, DateTime maxValue)
    {
      var dialog = new SetTimeDialog();

      SetTimeDialogSettings dialogSettings = _state.Settings.SetTimeDialogSettings;

      dialog.Text = "Set Time";
      dialog.DateFormatString = dialogSettings.DateFormatString;
      dialog.SlideDistance = dialogSettings.SlideDistance;
      dialog.ThumbsBackgroundColor = ColorConverter.ConvertFrom(dialogSettings.ThumbsBackgroundColor);
      dialog.ThumbsDownColor = ColorConverter.ConvertFrom(dialogSettings.DownThumbsColor);
      dialog.ThumbsPadding = dialogSettings.ThumbsPadding;

      dialog.CultureInfo = _state.Settings.CultureInfo;

      dialog.MinValue = minValue;
      dialog.MaxValue = maxValue;
      dialog.Value = DateTime.Now;

      return dialog;
    }


    public static List<ActivityRuleSettingsControlClassInfo> GetActivityRuleSettingsControlsClassInfos()
    {
      if (_ruleSettingsControlsClassInfos != null)
      {
        return _ruleSettingsControlsClassInfos;
      }

      _ruleSettingsControlsClassInfos =
        GetClassInfos<ActivityRuleSettingsControlClassInfo>(typeof(ActivityRuleSettingsControlAttribute));

      return _ruleSettingsControlsClassInfos;
    }


    public static List<ApplicationClassInfo> GetApplicationViewClassInfos()
    {
      if (_viewClassInfos != null)
      {
        return _viewClassInfos;
      }

      _viewClassInfos = GetClassInfos<ApplicationClassInfo>(typeof(ApplicationViewAttribute));

      return _viewClassInfos;
    }


    /// <summary>
    /// Gets the name of the executable file.
    /// </summary>
    /// <returns>
    /// A path to the application executable file.
    /// </returns>
    public static string GetExecutableFileName()
    {
      return Assembly.GetExecutingAssembly().ManifestModule.FullyQualifiedName;
    }


    /// <summary>
    /// Gets the rule class infos.
    /// </summary>
    /// <returns>
    /// </returns>
    public static List<ActivityRuleClassInfo> GetRuleClassInfos()
    {
      if (_ruleClassInfos != null)
      {
        return _ruleClassInfos;
      }

      _ruleClassInfos = GetClassInfos<ActivityRuleClassInfo>(typeof(ActivityRuleAttribute));

      return _ruleClassInfos;
    }


    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [MTAThread]
    [UsedImplicitly]
    public static void Main()
    {
      _state = LoadState();

      var loggers = new List<ILogger>();
      loggers.Add(new FileLogger(GetReportFileName(), "\t"));

      var processor = new Processor(loggers, _state);
      processor.AttachAgent(new CategoriesAgent());
      processor.AttachAgent(new RulesAgent());

      var window = new MainWindow(loggers, _state);

      Application.Run(window);

      processor.DetachAllAgents();

      SaveState(_state);
    }


    #endregion


    #region Methods


    private static List<T> GetClassInfos<T>(Type attributeType) where T: ApplicationClassInfo, new()
    {
      var classInfos = new List<T>();

      Assembly assembly = Assembly.GetExecutingAssembly();
      Type[] types = assembly.GetTypes();

      foreach (Type type in types)
      {
        object[] attributes = type.GetCustomAttributes(attributeType, true);

        if (attributes == null || attributes.Length == 0)
        {
          continue;
        }

        var attribute = (ApplicationClassAttribute)attributes[0];

        var item = new T();
        item.Type = type;
        item.Attribute = attribute;

        classInfos.Add(item);
      }

      return classInfos;
    }


    [NotNull]
    private static string GetCurrentDirectory()
    {
      string currentDirectory = GetExecutableFileName();
      currentDirectory = Path.GetDirectoryName(currentDirectory);

      return currentDirectory;
    }


    [NotNull]
    private static string GetReportFileName()
    {
      string currentDirectory = GetCurrentDirectory();
      return Path.Combine(currentDirectory, "Report.txt");
    }


    [NotNull]
    private static string GetStateFileName()
    {
      string currentDirectory = GetCurrentDirectory();
      return Path.Combine(currentDirectory, "State.xml");
    }


    /// <summary>
    /// Loads the settings.
    /// </summary>
    /// <returns>
    /// Returns an instance of <see cref="ApplicationState"/>.
    /// </returns>
    /// <exception cref="Exception">
    /// <c>Exception</c>.
    /// </exception>
    [NotNull]
    private static ApplicationState LoadState()
    {
      string stateFileName = GetStateFileName();

      if (!File.Exists(stateFileName))
      {
        return new ApplicationState();
      }

      Stream stream = null;
      ApplicationState applicationState;

      try
      {
        stream = new FileStream(stateFileName, FileMode.Open, FileAccess.Read);

        var serializer = new XmlSerializer(typeof(ApplicationState));
        applicationState = (ApplicationState)serializer.Deserialize(stream);
      }
      catch (Exception e)
      {
        MessageBox.Show(e.Message, "Error Loading Application State", MessageBoxButtons.OK, MessageBoxIcon.Hand, 
                        MessageBoxDefaultButton.Button1);

        Application.Exit();

        throw;
      }
      finally
      {
        if (stream != null)
        {
          stream.Close();
        }
      }

      return applicationState;
    }


    private static void SaveState([NotNull] ApplicationState applicationState)
    {
      Assert.ArgumentNotNull(applicationState, "applicationState");

      string stateFileName = GetStateFileName();

      Stream stream = null;

      try
      {
        stream = new FileStream(stateFileName, FileMode.Create, FileAccess.Write);

        var serializer = new XmlSerializer(typeof(ApplicationState));
        serializer.Serialize(stream, applicationState);
      }
      catch (Exception e)
      {
        MessageBox.Show(e.Message, "Error Saving Application State", MessageBoxButtons.OK, MessageBoxIcon.Hand, 
                        MessageBoxDefaultButton.Button1);
      }
      finally
      {
        if (stream != null)
        {
          stream.Close();
        }
      }
    }


    #endregion
  }
}